package com.mofang.ticketing.base.serve.file.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.mofang.ticketing.base.mapping.entity.SysAttachmentDO;
import com.mofang.ticketing.base.mapping.mapper.SysAttachmentDOMapper;
import com.mofang.ticketing.base.serve.file.pojo.response.AttachmentRespDTO;
import com.mofang.ticketing.base.serve.file.pojo.response.AttachmentUrlByIdsRespDTO;
import com.mofang.ticketing.base.serve.file.service.IAttachmentManageService;
import com.mofang.ticketing.common.core.enums.base.AttachmentDataTypeEnum;
import com.mofang.ticketing.common.core.enums.base.AttachmentStatusEnum;
import com.mofang.ticketing.common.core.enums.base.AttachmentTypeEnum;
import com.mofang.ticketing.common.core.util.FileUtil;
import com.mofang.ticketing.common.file.util.AliyunOSSUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author LGC
 * @date 2021/7/30 9:42
 * @copyright 2021 mofang. All rights reserved
 */
@Slf4j
@Service
public class AttachmentManageServiceImpl implements IAttachmentManageService {
    @Resource
    private AliyunOSSUtils aliyunOSSUtils;
    @Resource
    private SysAttachmentDOMapper attachmentMapper;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AttachmentRespDTO uploadAttachment(MultipartFile file) {
        AttachmentRespDTO attachmentRespDTO = new AttachmentRespDTO();
        String url = aliyunOSSUtils.uploadMultipartFile(file);
        if (StrUtil.isNotBlank(url)) {
            SysAttachmentDO attachment = new SysAttachmentDO();
            attachment.setType(0);
            attachment.setRefId(0);
            attachment.setUrl(url);
            //暂时默认为图片
            attachment.setAttachmentType(AttachmentTypeEnum.getAttachmentType(url).getKey());
            attachment.setUpdateTime(new Date());
            attachmentMapper.insert(attachment);
            attachmentRespDTO.setId(attachment.getId());
            attachmentRespDTO.setUrl(url);
            attachmentRespDTO.setRelativePath(FileUtil.relativePath(url));
        }
        return attachmentRespDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delAttachment(Integer dayOffSet) {
        try {
            LambdaQueryWrapper<SysAttachmentDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SysAttachmentDO::getType, 0)
                    .or().eq(SysAttachmentDO::getStatus, AttachmentStatusEnum.INVALID.getKey())
                    .apply("date_format(update_time,'%Y-%m-%d') <= {0}", DateUtil.formatDate(DateUtil.offsetDay(DateUtil.date(), -dayOffSet)));
            List<SysAttachmentDO> attachments = attachmentMapper.selectList(queryWrapper);
            if (CollectionUtil.isNotEmpty(attachments) && attachments.size() > 0) {
                Set<String> pathSet = attachments.stream().map(a -> FileUtil.relativePath(a.getUrl())).collect(Collectors.toSet());

                Future<Integer> future = threadPoolExecutor.submit(() -> {
                    int ind = 0;
                    for (String path : pathSet) {
                        aliyunOSSUtils.deleteFile(path);
                        ind++;
                    }
                    return ind;
                });
                if (future.get() == pathSet.size()) {
                    log.info("需要删除oss附件条数：{}", future.get());
                    List<Integer> ids = attachments.stream().map(SysAttachmentDO::getId).collect(Collectors.toList());
                    attachmentMapper.deleteBatchIds(ids);
                    log.info("删除数据库附件成功:{}", ids.size());
                }
            }
        } catch (Exception e) {
            log.error("删除附件异常{0}", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAttachment(List<String> attachmentUrls, Integer relId, AttachmentDataTypeEnum attachmentDataTypeEnum) {
        attachmentInvalid(attachmentDataTypeEnum, relId);
        attachmentValid(attachmentUrls, relId, attachmentDataTypeEnum);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void attachmentInvalid(AttachmentDataTypeEnum attachmentEnum, Integer refId) {
        new LambdaUpdateChainWrapper<>(attachmentMapper)
                .set(SysAttachmentDO::getStatus, AttachmentStatusEnum.INVALID.getKey())
                .set(SysAttachmentDO::getUpdateTime, new Date())
                .eq(SysAttachmentDO::getType, attachmentEnum.getKey())
                .eq(SysAttachmentDO::getRefId, refId).update();
    }

    @Override
    public void attachmentInvalid(AttachmentDataTypeEnum attachmentEnum, List<Integer> relIdList) {
        new LambdaUpdateChainWrapper<>(attachmentMapper)
                .set(SysAttachmentDO::getStatus, AttachmentStatusEnum.INVALID.getKey())
                .set(SysAttachmentDO::getUpdateTime, new Date())
                .eq(SysAttachmentDO::getType, attachmentEnum.getKey())
                .in(SysAttachmentDO::getRefId, relIdList).update();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void attachmentValid(List<String> attachmentUrls, Integer refId, AttachmentDataTypeEnum attachmentEnum) {
        List<Integer> idList = attachmentIdList(attachmentUrls);
        if (CollectionUtils.isNotEmpty(idList)) {
            List<SysAttachmentDO> attachmentList = attachmentMapper.selectBatchIds(idList);
            attachmentList.forEach(a -> {
                a.setType(attachmentEnum.getKey());
                a.setRefId(refId);
                a.setStatus(AttachmentStatusEnum.VALID.getKey());
                a.setUpdateTime(new Date());
            });
            attachmentMapper.updateBatchSelective(attachmentList);
        }
    }

    @Override
    public List<String> getAttachmentUrlList(Integer relId, AttachmentDataTypeEnum attachmentEnum) {
        LambdaQueryWrapper<SysAttachmentDO> attachmentQueryWrapper = new LambdaQueryWrapper<>();
        attachmentQueryWrapper.eq(SysAttachmentDO::getType, attachmentEnum.getKey())
                .eq(SysAttachmentDO::getRefId, relId)
                .eq(SysAttachmentDO::getStatus, AttachmentStatusEnum.VALID.getKey());
        return attachmentMapper.selectList(attachmentQueryWrapper)
                .stream().map(SysAttachmentDO::getUrl).collect(Collectors.toList());
    }

    @Override
    public List<AttachmentUrlByIdsRespDTO> getAttachmentUrlList(List<Integer> relIds, AttachmentDataTypeEnum attachmentEnum) {
        List<SysAttachmentDO> sysAttachmentDOS = new LambdaQueryChainWrapper<>(attachmentMapper)
                .eq(SysAttachmentDO::getType, attachmentEnum.getKey())
                .in(SysAttachmentDO::getRefId, relIds)
                .eq(SysAttachmentDO::getStatus, AttachmentStatusEnum.VALID.getKey()).list();
        if (CollectionUtils.isNotEmpty(sysAttachmentDOS)) {
            Map<Integer, List<SysAttachmentDO>> sysAttachmentMap = sysAttachmentDOS.stream().collect(Collectors.groupingBy(SysAttachmentDO::getRefId));
            List<AttachmentUrlByIdsRespDTO> resultList = new ArrayList<>();
            sysAttachmentMap.forEach((k, v) -> {
                AttachmentUrlByIdsRespDTO attachmentUrlByIdsRespDTO = new AttachmentUrlByIdsRespDTO();
                attachmentUrlByIdsRespDTO.setRefId(k);
                attachmentUrlByIdsRespDTO.setUrls(v.stream().map(SysAttachmentDO::getUrl).collect(Collectors.toList()));
                resultList.add(attachmentUrlByIdsRespDTO);
            });
            return resultList;
        }
        return null;
    }


    /**
     * 根据附件url
     * 获取附件ID
     *
     * @param attachmentUrls
     * @return
     */
    private List<Integer> attachmentIdList(List<String> attachmentUrls) {
        if (CollectionUtil.isNotEmpty(attachmentUrls)) {
            return new LambdaQueryChainWrapper<>(attachmentMapper)
                    .in(SysAttachmentDO::getUrl, attachmentUrls).list()
                    .stream().map(SysAttachmentDO::getId).collect(Collectors.toList());
        }
        return null;
    }
}
